/*
* @Author: z.p
* @Project: Zdotp
* @Date: 2024年12月30日
* @Description: actor基类, 每个actor放入一个线程 actor之间使用mailBox(发送消息)的方式交互
*/

using Zdotp.Actor.Components;
using Zdotp.Actor.Controller;
using Zdotp.Actor.Group;
using Zdotp.Actor.Packet;
using Zdotp.Logs;
using Zdotp.Timer;

namespace Zdotp.Actor;

public abstract class ActorBase
{
    #region lifeTime

    public int ActorId;
    protected string GetActorName() => GetType().Name;
    protected ZGroup Group;

    public ActorSystem MyActorSystem;
    public ZLogger Logger;
    
    public void Awake(int actorId, ActorSystem actorSystem, ZGroup group)
    {
        ActorId = actorId;
        MyActorSystem = actorSystem;
        Group = group;
        
        Logger = LogHelper.CreateLogger(GetActorName());
        
        var assembly = GetType().Assembly;
        foreach (var classType in assembly.GetTypes())
        {
            var baseType = classType.BaseType;
            if (baseType == null) continue;
            if (!baseType.IsGenericType || baseType.GetGenericTypeDefinition() != typeof(ActorController<,>)) continue;
            var genericArguments = baseType.GetGenericArguments();
            if (genericArguments.Length != 2) continue;
            var actorType = genericArguments[0];
            var messageType = genericArguments[1];
            if (actorType != GetType()) continue;
            
            MyActorSystem.RegisterMessage(messageType, ActorId);
        }
        
        OnAwake();
    }
    protected abstract void OnAwake();
    
    public void Start()
    {
        Logger.Info($"[ {GetActorName()} ] Runs in group {Group.GroupId}...");
        OnStart();
    }

    protected abstract void OnStart();
    
    // Logger.Info($"{GetActorName()} Actor Start, port : {_port}, Runs in group {groupId}...");
    
    private readonly Lock _locker = new();
    public void Update(float deltaTime)
    {
        lock (_locker)
        {
            while (_packetQueue.Count > 0)
            {
                var packet = _packetQueue.Dequeue();
                
                var info = packet.Info;
                var data = packet.Data;
                var type = packet.DataType;
                
                if (type == null || data == null)
                {
                    Logger.Error($"{GetActorName()} receive null packet!");
                    continue;
                }

                ActorSystem.ActorControl(type, this, info, data);
                
                // 回收packet
                ActorSystem.ReleaseObj(packet);
            }
        }

        for (var index = _actorTimers.Count - 1; index >= 0; index--)
        {
            var timer = _actorTimers[index];
            timer.Update(deltaTime);
        }
        
        for (var index = _components.Count - 1; index >= 0; index--)
        {
            var component = _components[index];
            component.OnUpdate(deltaTime);
        }
        OnUpdate(deltaTime);
    }

    protected abstract void OnUpdate(float deltaTime);

    public void Destroy()
    {
        OnDestroy();
        foreach (var timer in _actorTimers)
        {
            timer.StopTimer();
        }
        _actorTimers.Clear();
        foreach (var component in _components)
        {
            component.OnRemove();
        }
        _components.Clear();
    }

    protected abstract void OnDestroy();

    #endregion
    
    #region Component

    private readonly List<ComponentBase> _components = [];

    public T AddComponent<T>() where T : ComponentBase
    {
        var type = typeof(T);
        return (T)AddComponent(type);
    }

    public T GetOrAddComponent<T>() where T : ComponentBase
    {
        var type = typeof(T);
        foreach (var comp in _components)
        {
            if (comp.GetType() == type) return (T)comp;
        }
        
        return AddComponent<T>();
    }

    public ComponentBase AddComponent(Type componentType)
    {
        if (componentType.BaseType == null || componentType.BaseType != typeof(ComponentBase))
        {
            throw new Exception($"component {componentType.Name} not inherit ComponentBase");
        }

        foreach (var comp in _components)
        {
            if (comp.GetType() == componentType) return comp;
        }
        
        // Logger.Info($"register component : [ actorId: {ActorId}, componentType: {componentType.Name} ]");
        
        var component = (ComponentBase)Activator.CreateInstance(componentType)!;
        _components.Add(component);
        component.OnInit(this);
        return component;
    }
    
    public void RemoveComponent<T>() where T : ComponentBase
    {
        if (_components.Count == 0) return;
        var type = typeof(T);
        for (var index = _components.Count - 1; index >= 0; index--)
        {
            var comp = _components[index];
            if (comp.GetType() == type)
            {
                comp.OnRemove();
                _components.RemoveAt(index);
                return;
            }
        }
    }

    #endregion

    #region Message
    
    private readonly Queue<Packet.Message> _packetQueue = new();

    public void SendMessage<T>(Action<T> onInit, int receiveActorId = -1, uint sessionId = 0) where T : MailObject, new()
    {
        var mail = MyActorSystem.CreateMail(onInit);
        MyActorSystem.ReceiveMessage(mail, ActorId, receiveActorId, sessionId);
    }
    
    public void ReceiveMessage<T>(T data, int senderActorId = -1, uint sessionId = 0) where T : MailObject
    {
        var messageInfo = new MessageInfo(senderActorId, ActorId, sessionId);

        lock (_locker)
        {
            var actorPacket = ActorSystem.GetObj<Message>(msg =>
            {
                msg.Info = messageInfo;
                msg.SetData(data);
            });
            if (actorPacket == null)
            {
                Logger.Error($"{GetActorName()} receive null packet!");
                return;
            }
            _packetQueue.Enqueue(actorPacket);
        }
    }

    #endregion

    #region Timer

    private int _timerId;
    private int MaxTimerId
    {
        get
        {
            if (_timerId > int.MaxValue / 2) _timerId = 0;
            return ++ _timerId;
        }
    }
    
    private readonly List<ZTimer> _actorTimers = [];
    public void StartTimer(ZTimer zTimer)
    {
        zTimer.TimerId = MaxTimerId;
        _actorTimers.Add(zTimer);
    }

    public void StopTimer(ZTimer zTimer)
    {
        _actorTimers.Remove(zTimer);
    }

    #endregion
}